/* Copyright (C) 2003, Charles Wang.
 * Author:  Charles Wang <charles@linux.net.cn>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANT; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public LIcense for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, 59 Temple
 * Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include  <sys/mount.h>
#include  <errno.h>
#include  <stdio.h>
#include  <stdlib.h>
#include  <string.h>
#include  <unistd.h>
#include  <newt.h>
#include  <kudzu/kudzu.h>
#include  "mount_guess_fstype.h"
#include  "magicbooter.h"
#include  "mblog.h"
#include  "utils.h"

extern const char * bootiso_fn;
extern const char * hdpathes[];
extern unsigned     num_hdpathes;
extern const char * cdpathes[];
extern unsigned     num_cdpathes;

/* Return true when the provided checkval is in result_list. */
/*static int  rl_has_path(const char *checkval);*/

typedef struct {
    char hd0_cd1;
    char device[6];
}  partition;

static unsigned calc_search_part_weight(unsigned *partnum,
					partition *partbuf, unsigned pbmax);
static unsigned probe_part(partition     * part,
#if !defined(NONEWT)
			   newtComponent   label,
			   newtComponent   scale,
#endif  /* NONEWT */
			   unsigned        process);

const char * search_runner(void) {
    unsigned        partnum;
    partition       partbuf[512], *curpart;
    unsigned        cur, weight;
#if !defined(NONEWT)
    newtComponent   label, scale;
#endif

    relist_clear();
    weight = calc_search_part_weight(&partnum, partbuf,
				     sizeof(partbuf) / sizeof(partbuf[0]));

    newtCenteredWindow(52, 5, "Searching the Full Run Environment");
#if !defined(NONEWT)
    label = newtLabel(1, 1, "");
    scale = newtScale(1, 3, 50, weight, 0);
    newtScaleSet(scale, weight);
    newtRefresh();
#endif

    cur = 0;
    for (curpart = partbuf; curpart - partbuf < partnum; ++curpart) {
#if !defined(NONEWT)
	cur = probe_part(curpart, label, scale, cur);
#else                         /* NONEWT */
	cur = probe_part(curpart, cur);
#endif
	/*mblog("process = %d, curpath->device = %s\n", cur, curpart->device);*/
    }

#if !defined(NONEWT)
    sleep(1);
    newtPopWindow();
#else                         /* NONEWT */
    printf("\n");
#endif

    return  NULL;
}


static unsigned calc_search_part_weight(unsigned *partnum,
					partition *partbuf, unsigned pbmax) {
    FILE           *pfp;
    char           linebuf[256], *p0, device[4];
    struct device **disks;  int i;
    partition      *curpart;
    unsigned        weight;

    weight = 0;
    curpart = partbuf;

    disks = probeDevices(CLASS_CDROM | CLASS_HD, BUS_IDE | BUS_SCSI, PROBE_ALL);
    if (disks == NULL)  goto ret0;

    for (i = 0; curpart - partbuf < pbmax && disks[i] != NULL; ++i) {
	if (disks[i]->type & CLASS_CDROM) {
	    weight += 25 + num_cdpathes;
	    curpart->hd0_cd1 = 1;
	    snprintf(curpart->device, sizeof(curpart->device), "%s", disks[i]->device);
	    ++curpart;
	}
    }
    pfp = fopen("/proc/partitions", "r");
    if (pfp == NULL)  goto ret1;

    device[3] = 0;
    fgets(linebuf, sizeof(linebuf), pfp); /* Skip the title line. */
    fgets(linebuf, sizeof(linebuf), pfp); /* Skip the blank line. */
    while (curpart - partbuf < pbmax &&
	   fgets(linebuf, sizeof(linebuf), pfp) != NULL) {
	p0 = linebuf + strspn(linebuf, " \t0123456789");
	memcpy(device, p0, 3); /* Tail 0 is always on device[3]. */
	p0[strcspn(p0, " \t\n")] = 0;
	for (i = 0; disks[i] != NULL; ++i)
	    if (!strcmp(device, disks[i]->device)) {
		if (disks[i]->type & CLASS_CDROM)  continue;
		if (strlen(p0) <= 3)  continue;
		/* A harddisk partition is found. */
		weight += num_hdpathes * (1 + num_cdpathes);
		curpart->hd0_cd1 = 0;
		snprintf(curpart->device, sizeof(curpart->device), "%s", p0);
		++curpart;
		break;
	    }
    }
    *partnum = curpart - partbuf;

    fclose(pfp);
 ret1:
    for (i = 0; disks[i] != NULL; ++i)
	freeDevice(disks[i]);
    free(disks);
 ret0:
    return  weight;
}

#define  LABELPREFIX  "Probe: "
#define  PARTWEIGHT(part) \
        ((part)->hd0_cd1 ? (5 + num_cdpathes) : (num_hdpathes * (1 + num_cdpathes)))

#if !defined(NONEWT)
static void newtLabelSetLimitedText(newtComponent label, const char *text) {
    char labelbuf[51];

    snprintf(labelbuf, sizeof(labelbuf), "%s", text);
    newtLabelSetText(label, labelbuf);
    printf("%s\n", labelbuf);
}
#endif

#if !defined(NONEWT)
static unsigned probe_part(partition     * part,
			   newtComponent   label,
			   newtComponent   scale,
			   unsigned        process) {
#else                         /* NONEWT */
static unsigned probe_part(partition     * part,
			   unsigned        process) {

#endif
    const char  *fstype;
    char  labelbuf[256], *realpath, pathbuf[300], *accpath;
    const char **pathes;  unsigned num_pathes;
    int   i, j;

    mblog("Probe: %s(%s)\n", part->device, part->hd0_cd1 ? "cdrom" : "harddisk partition");

    pathes = part->hd0_cd1 ? cdpathes : hdpathes;
    num_pathes = part->hd0_cd1 ? num_cdpathes : num_hdpathes;
    realpath = labelbuf + strlen(LABELPREFIX);

    snprintf(labelbuf, sizeof(labelbuf), "Detect and Mount /dev/%s", part->device);
#if !defined(NONEWT)
    newtLabelSetLimitedText(label, labelbuf);  newtRefresh();
    newtScaleSet(scale, process);  newtRefresh();
#else                         /* NONEWT */
    printf("%s\n", labelbuf);
#endif

    /* Mount the device for optimize firstly. */
    snprintf(pathbuf, sizeof(pathbuf), "/dev/%s", part->device);
    fstype = do_guess_fstype(pathbuf);
    if (!fstype) {
	mblog("  Detect filesystem type on [%s] failed.\n", pathbuf);
	return  process + PARTWEIGHT(part);
    }

    if (mount(pathbuf, search_device_root, fstype, MS_RDONLY, NULL)) {
	mblog("  Mount(%s, %s, %s, MS_RDONLY, NULL) failed: %s\n",
	      pathbuf, search_device_root, fstype, strerror(errno));
	return  process + PARTWEIGHT(part);
    }

    mblog("%s is mounted on %s as %s SUCESSFULLY.\n",
	  pathbuf, search_device_root, fstype);
    if (part->hd0_cd1) {
	process += 20;
#if !defined(NONEWT)
	newtScaleSet(scale, process);  newtRefresh();
#endif
    }

    for (i = 0; i < num_pathes; ++i) {
	/* Show what I'm doing. */
	snprintf(labelbuf, sizeof(labelbuf),
		 LABELPREFIX "/dev/%s%s/%s", part->device, pathes[i], mi_initrd_file);
#if !defined(NONEWT)
	newtLabelSetLimitedText(label, labelbuf);
	newtScaleSet(scale, process);  newtRefresh();
#else                         /* NONEWT */
    printf("%s\n", labelbuf);
#endif
	/* Do it. */
	snprintf(pathbuf, sizeof(pathbuf),
		 search_device_root "%s/%s", pathes[i], mi_initrd_file);
	mblog("  Position:%s,\n    realpath = %s):\n", pathbuf, realpath);
	if ((accpath = probe_path(pathbuf))) {
	    mblog("An miinitrd is found: %s\n", accpath);
	    free(accpath);
	    relist_append(realpath);
	} else
	    mblog("Failed\n");
	++process;
        /* probe a hotfix file */
        if (!mi_hotfix_fullpath[0]) {
            snprintf(pathbuf, sizeof(pathbuf),
                     search_device_root "%s/%s%s", pathes[i], bootiso_fn, mi_hotfix_postfix);
            mblog("Probe hotfix: %s\n", pathbuf);
            if ((accpath = probe_path(pathbuf))) {
                mblog("A hotfix is found: %s\n", accpath);
                free(accpath);
                snprintf(mi_hotfix_fullpath, sizeof(mi_hotfix_fullpath),
                         "/dev/%s%s/%s%s", part->device, pathes[i], bootiso_fn, mi_hotfix_postfix);
            }
        }

	if (part->hd0_cd1)  continue;
	/* User can put .iso images into harddisk partitions. */
	for (j = 0; j < num_cdpathes; ++j) {
	    /* Show what I'm doing. */
	    snprintf(labelbuf, sizeof(labelbuf),
		     LABELPREFIX "/dev/%s%s/%s%s/%s",
		     part->device, pathes[i], bootiso_fn, cdpathes[j], mi_initrd_file);
#if !defined(NONEWT)
	    newtLabelSetLimitedText(label, labelbuf);
	    newtScaleSet(scale, process);  newtRefresh();
#else                         /* NONEWT */
    printf("%s\n", labelbuf);
#endif
	    /* Do it. */
	    snprintf(pathbuf, sizeof(pathbuf),
		     search_device_root "%s/%s%s/%s",
		     pathes[i], bootiso_fn, cdpathes[j], mi_initrd_file);
	    mblog("  Position(%s,\n    realpath = %s): ", pathbuf, realpath);
	    if ((accpath = probe_path(pathbuf))) {
		mblog("%s\n", accpath);
		free(accpath);
		relist_append(realpath);
	    } else
		mblog("Failed\n");
	    ++process;
	}
    }

    umount(search_device_root);
    if (part->hd0_cd1) {
	process += 5;
#if !defined(NONEWT)
	newtScaleSet(scale, process);  newtRefresh();
#endif
    }

    return  process;
}
